#!/usr/bin/env python

DOCUMENTATION = """
---
module: mongodb_step_down
short_description: Issues a stepdown on the primary.
description:
  - Issues replSetStepDown on the host provided in host:.
    Afterwards, loops several times to ensure that a new primary is elected and that
    it is a different host than the previous primary.  Errors if the stepdown fails
    or if the cluster fails to elect a new primary.
version_added: "1.9"
author:
  - Kevin Falcone
options:
  host:
    description:
      - The hostname or ip of a server in the mongo cluster.
    required: false
    default: 'localhost'
  port:
    description:
      - The port to connect to mongo on.
    required: false
    default: 27017
  username:
    description:
      - The username of the mongo user to connect as.
    required: false
  password:
    description:
      - The password to use when authenticating.
    required: false
  auth_database:
    description:
      - The database to authenticate against.
    required: false
"""

EXAMPLES = '''
- name: Get status for the stage cluster
  mongodb_step_down
    host: localhost:27017
    username: root
    password: password

'''
# Magic import
from ansible.module_utils.basic import *

try:
    from pymongo import MongoClient
    from pymongo.errors import AutoReconnect
    from bson import json_util
except ImportError:
    pymongo_found = False
else:
    pymongo_found = True

import json
from urllib import quote_plus

def main():

    arg_spec = dict(
        host=dict(required=False, type='str', default="localhost"),
        port=dict(required=False, type='int', default=27017),
        username=dict(required=False, type='str'),
        password=dict(required=False, type='str'),
        auth_database=dict(required=False, type='str')
    )

    module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=False)

    if not pymongo_found:
        module.fail_json(msg="The python pymongo module is not installed.")

    mongo_uri = 'mongodb://'
    host = module.params.get('host')
    port = module.params.get('port')
    username = module.params.get('username')
    password = module.params.get('password')
    auth_database = module.params.get('auth_database')

    if (username and not password) or (password and not username):
        module.fail_json(msg="Must provide both username and password or neither.")

    if username:
        mongo_uri += "{}:{}@".format(*map(quote_plus, [username,password]))

    mongo_uri += "{}:{}".format(quote_plus(host),port)

    if auth_database:
        mongo_uri += '/{}'.format(quote_plus(auth_database))

    client = MongoClient(mongo_uri)

    # This has no return since it forces a disconnect or throws an error
    # about being unable to elect a secondary.  We only catch the AutoReconnect
    # so we see any other errors bubble up.
    try:
        client.admin.command("replSetStepDown",60,secondaryCatchUpPeriodSecs=30)
    except AutoReconnect:
        pass

    for i in range(5):
        status = client.admin.command("replSetGetStatus")
        primary = [m for m in status['members'] if m['stateStr'] == 'PRIMARY']
        # This won't work as well if you mix hostnames and IPs in your cluster.
        # We use only IPs.
        if primary and primary[0]['name'] != "{}:{}".format(quote_plus(host),port):
            module.exit_json(changed=True, stepdown=True)
        time.sleep(2)

    module.fail_json(msg="Unable to step down {}".format(host))

if __name__ == '__main__':
    main()
